use super::{check_sum, Error, Proto};

pub trait BytesCodec {
    type Err;

    unsafe fn to_bytes(self, ptr: *mut u8) -> usize;

    fn from_bytes(buf: &[u8]) -> Result<Self, Self::Err>
    where
        Self: Sized;
}

impl BytesCodec for Proto {

    type Err = Error;

    unsafe fn to_bytes(self, ptr: *mut u8) -> usize {
        *ptr = 0xFF;
        *ptr.add(1) = 0xC1;
        *ptr.add(2) = self.cmd2;
        *ptr.add(3) = self.data1;
        *ptr.add(4) = self.data2;
        let sum = check_sum(ptr, 5);
        *ptr.add(5) = sum;
        *ptr.add(6) = 0x0D;
        *ptr.add(7) = 0x0A;
        8
    }

    fn from_bytes(buf: &[u8]) -> Result<Self, Self::Err>
    where
        Self: Sized,
    {
        let index = buf
            .iter()
            .position(|&v| v == 0xFF)
            .ok_or(Error::NoFindStart)?;

        let data = &buf[index..];
        if data.len() < 8 {
            return Err(Error::Invalid);
        }

        let mut s: u8 = 0;
        for v in &data[..5] {
            s = s.wrapping_add(*v);
        }

        let cmd2 = data[2];
        let data1 = data[3];
        let data2 = data[4];
        let sum = data[5];

        if sum != s {
            return Err(Error::CheckSum);
        }

        Ok(Self { cmd2, data1, data2 })
    }
}
